package com.onpositive.datamap.core.impl;

import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;

import com.onpositive.data.AbstractReader;
import com.onpositive.data.FilterTester;
import com.onpositive.data.ICanHaveProperty;
import com.onpositive.data.IPropertyConstants;
import com.onpositive.data.IPropertyMetadata;
import com.onpositive.data.IReadableObjectCollection;
import com.onpositive.data.diskstorage.AbstractObjectCollection;
import com.onpositive.data.diskstorage.ByteBufferSeekableAcess;
import com.onpositive.data.diskstorage.IPostInitReader;
import com.onpositive.data.diskstorage.StoredMetadata;
import com.onpositive.data.serializer.readers.ObjectCollectionReader;
import com.onpositive.data.units.DateWithAccuracy;
import com.onpositive.datamap.IDataPropertyValue;
import com.onpositive.datamap.core.IDataCollection;
import com.onpositive.datamap.core.IDataObject;
import com.onpositive.datamap.core.IDataProperty;
import com.onpositive.datamap.core.IImageObject;
import com.onpositive.datamap.core.IPropertyGroup;
import com.onpositive.datamap.values.impl.DateRange;
import com.onpositive.datamap.values.impl.ScalarRange;
import com.onpositive.datamap.values.impl.ValueSequence;
import com.onpositive.semantic.model.api.changes.IValueListener;
import com.onpositive.semantic.model.api.changes.ObjectChangeManager;
import com.onpositive.semantic.model.api.labels.LabelAccess;
import com.onpositive.semantic.model.api.labels.LabelProperty;
import com.onpositive.semantic.model.api.meta.BaseMeta;
import com.onpositive.semantic.model.api.property.CommonPropertyProvider;
import com.onpositive.semantic.model.api.property.DefaultProperty;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.semantic.model.api.property.IPropertyProvider;
import com.onpositive.semantic.model.api.query.IQueryExecutor;
import com.onpositive.semantic.model.api.query.Query;
import com.onpositive.semantic.model.api.query.QueryFilter;
import com.onpositive.semantic.model.api.query.QueryResult;
import com.onpositive.semantic.model.api.realm.IRealm;
import com.onpositive.semantic.model.api.realm.IResultUpdate;
import com.onpositive.semantic.model.api.realm.OrderedRealm;
import com.onpositive.semantic.model.api.realm.Realm;
import com.onpositive.semantic.model.groups.BasicGroupingOperators;
import com.onpositive.semantic.model.groups.BasicGroupingOperators.Group;

public class DataCollection implements IDataCollection, IPropertyProvider,
		IQueryExecutor {

	private long timeStamp;
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	AbstractObjectCollection collection;
	IDataProperty[] properties;
	HashMap<String, IDataProperty> pm = new HashMap<String, IDataProperty>();
	String name;

	private IImageObject image;
	protected IPropertyMetadata uid_prop;
	protected IPropertyMetadata name_prop;
	protected IPropertyMetadata sname_prop;
	protected IPropertyMetadata actuality;
	protected IPropertyMetadata imageLinkProperty;
	protected IPropertyMetadata imageCommentProperty;
	protected IPropertyMetadata mainImage;
	protected IPropertyMetadata previewImage;
	protected IPropertyMetadata text_prop;
	private final static String PROPERTY_GROUPS="prgroups";

	public AbstractObjectCollection getCollection() {
		return collection;
	}

	static HashSet<String> toIgnore = new HashSet<String>();

	static {
		toIgnore.add(IPropertyConstants.WWW_PICTURE_LINKS_PROPERTY_NAME);

		toIgnore.add(IPropertyConstants.WWW_PICTURE_COMMENTS);
		toIgnore.add(IPropertyConstants.ITEM_NAME_PROPERTY_METADATA_ID);
		toIgnore.add(IPropertyConstants.MAIN_PICTURE_PROPERTY_METADATA_ID);
		toIgnore.add(IPropertyConstants.PREVIEW_PICTURE_PROPERTY_METADATA_ID);
		toIgnore.add(IPropertyConstants.DOCUMENT_NAME_PROPERTY_METADATA_ID);
		toIgnore.add(IPropertyConstants.EXTERNAL_DEFAULT_PICTURE_PROPERTY_NAME);
		toIgnore.add(IPropertyConstants.PARENT_CATEGORIES_PROPERTY_NAME);
		toIgnore.add(IPropertyConstants.PICTURE_LIST_PROPERTY_NAME);
		toIgnore.add(IPropertyConstants.TEXT_CONTENT_PROPERTY_NAME);
		toIgnore.add(IPropertyConstants.DEFAULT_PICTURE);
		toIgnore.add(IPropertyConstants.ACTUALITY_PROPERTY_METADATA_ID);
	}

	public DataCollection(AbstractObjectCollection collection, String name,
			IImageObject image) {
		super();
		doInit(collection, name, image);
	}

	private void doInit(AbstractObjectCollection collection, String name,
			IImageObject image) {
		this.collection = collection;
		AbstractObjectCollection mm = (AbstractObjectCollection) collection;
		mm.setReaderInit(new IPostInitReader() {

			@Override
			public void configure(IPropertyMetadata m, AbstractReader<?> r) {
				String id = m.id();
				if (id != null) {
					if (r instanceof ICanHaveProperty) {
						ICanHaveProperty c = (ICanHaveProperty) r;
						c.seProperty(pm.get(id));

					}
				}
			}
		});
		this.name = name;
		this.image = image;
		this.imageLinkProperty = collection
				.getProperty(IPropertyConstants.WWW_PICTURE_LINKS_PROPERTY_NAME);
		this.imageCommentProperty = collection
				.getProperty(IPropertyConstants.WWW_PICTURE_COMMENTS);
		this.actuality = collection
				.getProperty(IPropertyConstants.ACTUALITY_PROPERTY_METADATA_ID);
		this.mainImage = collection
				.getProperty(IPropertyConstants.MAIN_PICTURE_PROPERTY_METADATA_ID);

		this.previewImage = collection
				.getProperty(IPropertyConstants.PREVIEW_PICTURE_PROPERTY_METADATA_ID);
		this.uid_prop = collection
				.getProperty(IPropertyConstants.DOCUMENT_NAME_PROPERTY_METADATA_ID);
		this.name_prop = collection
				.getProperty(IPropertyConstants.DOCUMENT_NAME_PROPERTY_METADATA_ID);
		this.sname_prop = collection
				.getProperty(IPropertyConstants.ITEM_NAME_PROPERTY_METADATA_ID);
		this.text_prop = collection
				.getProperty(IPropertyConstants.TEXT_CONTENT_PROPERTY_NAME);
		properties = buildProperties(collection);
		for (IDataProperty s : properties) {
			pm.put(s.id(), s);
		}
		if (mainImage != null) {
			mm.init(mainImage);
		}
		IPropertyGroup[] mmq = (IPropertyGroup[]) uid_prop.getValue(PROPERTY_GROUPS);
		if (mmq!=null){
			ignore=true;
			try{
			pgroups.add(Arrays.asList(mmq));
			}finally{
				ignore=false;
			}
		}
	}

	public DataCollection(byte[] byteArray) {
		ByteBufferSeekableAcess randomAccessFileAccess = new ByteBufferSeekableAcess(
				byteArray);
		IReadableObjectCollection<?>[] collection = new ObjectCollectionReader()
				.getCollection(randomAccessFileAccess);
		doInit((AbstractObjectCollection) collection[0], "Tanks", null);
	}
	IValueListener<Object> vl = new IValueListener<Object>() {
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		@Override
		public void valueChanged(Object oldValue, Object newValue) {
			IDataProperty q=(IDataProperty) newValue;
			updateMeta(q.getId());
		}
	};

	private IDataProperty[] buildProperties(AbstractObjectCollection collection2) {
		ArrayList<IDataProperty> p = new ArrayList<IDataProperty>();
		for (IPropertyMetadata m : collection2.properties()) {
			if (toIgnore.contains(m.id())) {
				continue;
			}
			final IDataProperty property = createProperty(m);

			ObjectChangeManager.addWeakListener(property, vl);
			if (property != null) {
				p.add(property);
			}
		}
		return p.toArray(new IDataProperty[p.size()]);
	}

	protected void updateMeta(String id) {
		
	}

	protected IDataProperty createProperty(IPropertyMetadata m) {

		String metaPropertyValue = (String) m
				.getMetaPropertyValue(IPropertyConstants.CHAR_PARSER_ID_LABEL);

		if (metaPropertyValue != null) {
			if (metaPropertyValue
					.equals(IPropertyConstants.PROP_KIND_DIMENSION)) {
				return new DimensionProperty(m, collection);
			}
			if (metaPropertyValue
					.equals(IPropertyConstants.PROP_KIND_VALUE_SEQUENCE)
					|| metaPropertyValue
							.equals(IPropertyConstants.PROP_KIND_STRING)
					|| metaPropertyValue
							.equals(IPropertyConstants.PROP_KIND_IDENT)) {
				return new SimpleProperty(m,
						(AbstractObjectCollection) collection);
			}
			if (metaPropertyValue.equals(IPropertyConstants.PROP_KIND_SCALAR)) {
				return new ScalarProperty(m, collection);
			}
			if (metaPropertyValue.equals(IPropertyConstants.PROP_KIND_DATE)) {
				return new DateProperty(m, collection);
			}
		}
		return null;
	}

	@Override
	public int getCount() {
		return collection.size();
	}

	@Override
	public IDataObject getObject(int num) {
		return createDataObject(num);
	}

	protected DataObject createDataObject(int num) {
		return new DataObject(this, num);
	}

	@Override
	public IDataProperty[] getProperties() {
		return properties;
	}

	@Override
	public String getName() {
		return name;
	}

	@Override
	public IImageObject getImage() {
		return image;
	}

	protected String getString(int num, IPropertyMetadata uid_prop2) {
		return (String) collection.getValue(num, uid_prop2);
	}

	protected byte[] getBytes(int num, IPropertyMetadata uid_prop2) {
		return (byte[]) collection.getValue(num, uid_prop2);
	}

	public IProperty getProperty(String name) {
		return getProperty(null, name);
	}

	@Override
	public IProperty getProperty(Object obj, String name) {
		if (obj instanceof DataObject || obj == null) {
			DataObject z = (DataObject) obj;
			if (z == null || z.collection == this) {
				IDataProperty iDataProperty = pm.get(name);
				if (iDataProperty == null) {
					return CommonPropertyProvider.INSTANCE.getProperty(obj,
							name);
				}
				return iDataProperty;
			}
		}

		return null;
	}

	@SuppressWarnings({ })
	@Override
	public Iterable<IProperty> getProperties(Object obj) {
		Iterable<IProperty> basicProps = basicProps(obj);
		return basicProps;
	}

	protected Iterable<IProperty> basicProps(Object obj) {
		if (obj instanceof DataObject) {
			DataObject z = (DataObject) obj;
			if (z.collection == this) {
				return (Collection) pm.values();
			}
		}
		if (obj == null) {
			return (Collection) pm.values();
		}
		return null;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public QueryResult execute(Query query, IResultUpdate async) {
		QueryFilter[] filters = query.getFilters();
		int count = this.getCount();
		BitSet bs = new BitSet(count);
		String value = null;
		for (QueryFilter f : filters) {
			String propId = f.getPropId();
			if (propId.equals(LabelProperty.INSTANCE.getId())) {
				value = f.getFilterConstraint().toString().toLowerCase();
				continue;
			}
			int s = decode(f.getFilterKind());
			Object c = f.getFilterConstraint();
			
			if (c instanceof Group) {
				Group b = (Group) c;
				c = b.getElement();
				if (c==null||c.toString().length()==0){
					c=null;
				}
			}
			IProperty property = getProperty(null, propId);
			if (property == null) {
				if (propId
						.endsWith(IPropertyConstants.DOCUMENT_NAME_PROPERTY_METADATA_ID)
						|| propId.equals("caption")) {
					property = new SimpleProperty(name_prop,
							(AbstractObjectCollection) collection) {

						/**
						 * 
						 */
						private static final long serialVersionUID = 1L;

						@Override
						public String simpleType() {
							return "string";
						}

						@Override
						public Object adaptFilterValue(String c) {
							return c;
						}
					};
				} else {
					continue;
				}
			}
			if (property instanceof IDataProperty && c instanceof String) {
				IDataProperty d = (IDataProperty) property;
				c = d.adaptFilterValue((String) c);
			}

			FilterTester filterTester = ((AbstractObjectCollection) collection)
					.getFilterTester(propId, f.getFilterConstraint(),
							f.getFilterKind());
			for (int a = 0; a < count; a++) {
				if (!bs.get(a) && !filterTester.accept(s, a, c)) {
					bs.set(a);
				}
			}
		}
		List<IDataObject> rs = new ArrayList<IDataObject>();
		for (int a = 0; a < count; a++) {
			if (!bs.get(a)) {
				IDataObject object = getObject(a);
				if (!object.isPrimaryObject()) {
					continue;
				}
				if (value != null) {
					if (!object.getCaption().toLowerCase().contains(value)) {
						continue;
					}
				}

				// if (RepositoryUtil.getThumbnailUrl(2, object)!=null){
				rs.add(object);
				// }
			}
		}
		HashMap<String, Object> av = null;
		String[] interestingColumns = query.getInterestingColumns();
		if (interestingColumns != null) {
			av = calcAggregators(query, bs, av, interestingColumns);
		}
		String groupBy = query.getGroupBy();
		if (query.getSorting() != null) {
			Collections.sort(rs, getComparator(query));
		}

		if (groupBy != null) {
			QueryResult group = group(rs, groupBy);
			if (query.getSorting() != null) {
				long l0 = System.currentTimeMillis();
				if (query.getSorting().equals(groupBy)
						|| query.getSorting()
								.equals(IPropertyConstants.DOCUMENT_NAME_PROPERTY_METADATA_ID)) {
					Arrays.sort(group.getResult());
					if (!query.isAscendingSort()) {
						ArrayList<Object> arrayList = new ArrayList<Object>(
								Arrays.asList(group.getResult()));
						Collections.reverse(arrayList);
						group.setResult(arrayList.toArray());
					}
				} else {
					Arrays.sort(group.getResult(),
							((Comparator) getGroupComparator(query)));
				}
				long l1 = System.currentTimeMillis();

				System.out.println(l1 - l0);
			}
			if (av != null) {
				for (String s : av.keySet()) {
					group.setAggregatorValue(s, av.get(s));
				}
			}
			return group;
		}

		Object cursorToStart = query.getCursorToStart();
		int offset = 0;
		if (cursorToStart != null && cursorToStart instanceof Integer) {
			Integer cursorToStart2 = (Integer) cursorToStart;
			offset = cursorToStart2;
		}
		int limit = query.getLimit();
		long size = (long) rs.size();

		rs = rs.subList(offset, Math.min(rs.size(), limit + offset));
		QueryResult d = new QueryResult(rs.toArray());
		d.setTotalcount(size);
		if (av != null) {
			for (String s : av.keySet()) {
				d.setAggregatorValue(s, av.get(s));
			}
		}
		if (offset + limit < size) {
			d.setCursor(offset + limit);
		}
		return d;
	}

	protected HashMap<String, Object> calcAggregators(Query query, BitSet rs,
			HashMap<String, Object> av, String[] interestingColumns) {
		for (String s : interestingColumns) {
			DataProperty property = (DataProperty) getProperty(s);

			//
			String aggregator = query.getAggregator(s);
			if (aggregator != null) {
				Object aggregate = property.aggregate(aggregator, rs);
				if (aggregate != null) {
					if (av == null) {
						av = new HashMap<String, Object>();
					}
					av.put(s, aggregate);
				}

			}
		}
		return av;
	}

	private int decode(String filterKind) {
		if (filterKind.equals(QueryFilter.FILTER_EQUALS)) {
			return FilterTester.EQ;
		}
		if (filterKind.equals(QueryFilter.FILTER_NOT_EQUALS)) {
			return FilterTester.NEQ;
		}
		if (filterKind.equals(QueryFilter.FILTER_LE)) {
			return FilterTester.LE;
		}
		if (filterKind.equals(QueryFilter.FILTER_LT)) {
			return FilterTester.LT;
		}
		if (filterKind.equals(QueryFilter.FILTER_GE)) {
			return FilterTester.GE;
		}
		if (filterKind.equals(QueryFilter.FILTER_GT)) {
			return FilterTester.GT;
		}
		if (filterKind.equals(QueryFilter.FILTER_ONE_OF)) {
			return FilterTester.ONE_OF;
		}
		if (filterKind.equals(QueryFilter.FILTER_CONTAINS)) {
			return FilterTester.CONTAINS;
		}
		if (filterKind.equals(QueryFilter.FILTER_STARTS_WITH)) {
			return FilterTester.STARTSWITH;
		}
		throw new UnsupportedOperationException();
	}

	@SuppressWarnings("deprecation")
	private QueryResult group(List<IDataObject> rs, String groupBy) {
		final IDataProperty p = (IDataProperty) getProperty(null, groupBy);
		Collection<Group> group;
		if (IPropertyConstants.PROP_KIND_SCALAR.equals(p.simpleType())
				|| IPropertyConstants.PROP_KIND_DIMENSION
						.equals(p.simpleType())
				|| IPropertyConstants.PROP_KIND_DATE.equals(p.simpleType())) {

			if (p instanceof DateProperty) {
				group = BasicGroupingOperators.group(rs,
						new ScalarRangeGroupingCalculator(10),
						new DefaultProperty("") {

							/**
							 * 
							 */
							private static final long serialVersionUID = 1L;

							@Override
							public Object getValue(Object obj) {
								Object value = p.getValue(obj);
								if (value == null) {
									return null;
								}
								DateRange rm = (DateRange) value;
								if (rm.isNever()) {
									return ScalarRange.NONE;
								}
								return new ScalarRange(null, null, rm.getLow()
										.getDate().getYear(), rm.getHigh()
										.getDate().getYear());
							}
						});
				for (Group e : group) {
					ScalarRange key = (ScalarRange) e.getKey();
					if (key == null) {
						continue;
					}
					if (key.isNone()) {
						DateWithAccuracy m0 = new DateWithAccuracy(0);
						e.setKey(new DateRange(p, "", m0));
					} else {
						Date s = new Date();
						s.setYear(key.getLow().intValue());
						DateWithAccuracy m0 = new DateWithAccuracy(s,
								DateWithAccuracy.ACCURACY_YEAR);
						Date s1 = new Date();
						s1.setYear(key.getHigh().intValue());
						DateWithAccuracy m1 = new DateWithAccuracy(s1,
								DateWithAccuracy.ACCURACY_YEAR);

						e.setKey(new DateRange(p, "", m0, m1));
					}
				}

			} else {
				group = BasicGroupingOperators.group(rs,
						new ScalarRangeGroupingCalculator(10), p);
			}
		} else
			group = BasicGroupingOperators.group(rs,
					new IdentityGroupingCalculator(), p);
		if (group != null && !group.isEmpty()) {
			Object[] array = group.toArray();
			Arrays.sort(array);
			QueryResult queryResult = new QueryResult(array);
			queryResult.setTotalcount((long) group.size());
			return queryResult;
		}
		HashSet<Group> values = new HashSet<Group>();
		for (IDataObject oz : rs) {
			IDataPropertyValue value = p.getValue(oz);
			if (value instanceof ValueSequence) {
				ValueSequence s = (ValueSequence) value;
				Object[] parts = s.getParts();
				for (Object o : parts) {
					values.add(new Group(o, p));
				}
			} else {
				values.add(new Group(value, p));
			}
		}
		QueryResult queryResult = new QueryResult(values.toArray());
		queryResult.setTotalcount(new Long(values.size()));
		return queryResult;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private Comparator<? super IDataObject> getComparator(final Query query) {
		String sorting = query.getSorting();
		if (sorting.equals("caption")) {
			return new Comparator<IDataObject>() {

				@Override
				public int compare(IDataObject o1, IDataObject o2) {
					return o1.getCaption().compareTo(o2.getCaption());
				}
			};
		}
		final FilterTester filterTester = ((AbstractObjectCollection) collection)
				.getFilterTester(sorting, null, null);
		Comparator<DataObject> d = new Comparator<DataObject>() {

			final boolean m = query.isAscendingSort();

			@Override
			public int compare(DataObject o1, DataObject o2) {
				if (filterTester == null) {
					int compare = o1.getCaption().compareTo(o2.getCaption());
					return m ? compare : -compare;
				}
				int compare = filterTester.compare(o1.num, o2.num);
				return m ? compare : -compare;
			}
		};
		
		return (Comparator) d;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private Comparator<? super IDataObject> getGroupComparator(final Query query) {
		String sorting = query.getSorting();
		final IProperty property = getProperty(sorting);
		Comparator<Group> d = new Comparator<Group>() {

			final boolean m = query.isAscendingSort();

			@Override
			public int compare(Group arg0, Group arg1) {
				Object value0 = arg0.getValue(property);
				Object value1 = arg1.getValue(property);
				try {
					if (value0 instanceof Comparable
							&& value1 instanceof Comparable || value1 == null) {
						int compare = ((Comparable) value0)
								.compareTo((Comparable) value1);
						return m ? compare : -compare;
					}
				} catch (Exception e) {

				}
				int compare = LabelAccess.getLabel(value0).toLowerCase().compareTo(
						LabelAccess.getLabel(value1).toLowerCase());
				return m ? compare : -compare;
			}
		};
		return ((Comparator) d);
	}

	@Override
	public void cancel(IResultUpdate async) {

	}

	IRealm<?> r = null;

	@SuppressWarnings("rawtypes")
	public IRealm<?> getRealm() {
		if (r != null) {
			return r;
		}
		r = new Realm() {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public Collection getContents() {
				Query query = new Query("");
				query.setLimit(Integer.MAX_VALUE);
				QueryResult execute = DataCollection.this.execute(query, null);
				return Arrays.asList(execute.getResult());
			}

			@Override
			public boolean contains(Object value) {
				if (value instanceof DataObject) {
					DataObject m = (DataObject) value;
					if (m.collection == DataCollection.this) {
						return true;
					}
				}
				return super.contains(value);
			}
		};
		((BaseMeta) r.getMeta()).registerService(IQueryExecutor.class, this);
		return r;
	}

	@Override
	public long getTimeStamp() {
		return timeStamp;
	}

	protected HashMap<String, Integer> im = new HashMap<String, Integer>();
	boolean inited;

	@Override
	public IDataObject getByName(String name) {
		if (inited) {
			Integer integer = im.get(name);
			if (integer != null) {
				return new DataObject(this, integer.intValue());
			}
		} else {
			for (int a = 0; a < collection.size(); a++) {
				Object value = collection.getValue(a, name_prop);
				String value2 = (String) value;

				im.put(value2, a);
				if (sname_prop != null) {
					value = collection.getValue(a, sname_prop);
					if (value != null) {
						String value3 = (String) value.toString();
						im.put(value3, a);
					}
				}
				inited = true;
			}
			return getByName(name);
		}
		return null;
	}

	public IDataObject[] findFuzzy(String name) {
		LinkedHashSet<IDataObject> ms = new LinkedHashSet<IDataObject>();
		for (int a = 0; a < collection.size(); a++) {
			Object value = collection.getValue(a, name_prop);
			String value2 = (String) value;
			if (contains(name, value2)) {
				ms.add(getObject(a));
			}

			if (sname_prop != null) {
				value = collection.getValue(a, sname_prop);
				if (!(value instanceof String)) {
					return new IDataObject[0];
				}
				String value3 = (String) value;
				if (contains(name, value3)) {
					ms.add(getObject(a));
				}
			}
		}
		return ms.toArray(new IDataObject[ms.size()]);
	}

	protected boolean contains(String name, String value2) {
		if (value2 == null) {
			return false;
		}
		String lowerCase = value2.toLowerCase();
		String lowerCase2 = name.toLowerCase();
		boolean contains = lowerCase.contains(lowerCase2);
		if (contains) {
			int indexOf = lowerCase.indexOf(lowerCase2);
			if (indexOf > 0) {
				char charAt = lowerCase.charAt(indexOf - 1);
				if (!Character.isWhitespace(charAt) || charAt == '_') {
					return false;
				}
			}
			int index = indexOf + lowerCase2.length();
			if (lowerCase.length() > index) {
				char charAt = lowerCase.charAt(index);
				if (!Character.isWhitespace(charAt) || charAt == '_') {
					return false;
				}
			}
		}
		return contains;
	}

	boolean isActual(int num) {
		if (actuality != null) {
			Object value = collection.getValue(num, actuality);
			if (value != null && value.equals("true")) {
				return true;
			}
			return false;
		}
		return true;
	}
	
	

	public void write(DataOutputStream s) throws IOException {
		collection.write(s);
	}

	public IDataProperty addProperty(IPropertyMetadata createScalarMeta) {
		collection.addNewMetadata(createScalarMeta);
		IDataProperty createProperty = createProperty(createScalarMeta);
		pm.put(createProperty.id(), createProperty);
		return createProperty;
	}

	public void removeProperty(IDataProperty prop) {
		DataProperty p = (DataProperty) prop;
		collection.removeMetadata(p.meta);
		pm.remove(prop);
	}

	public void addProperty(IDataProperty q) {
		DataProperty m = (DataProperty) q;
		addProperty(m.meta);
	}

	public IPropertyMetadata[] getSystemProperties() {
		return new IPropertyMetadata[] { uid_prop, name_prop, sname_prop,
				actuality, imageLinkProperty, imageCommentProperty };
	}
	boolean ignore=false;
	protected OrderedRealm<IPropertyGroup> pgroups = new OrderedRealm<IPropertyGroup>(){
		protected void fireDelta(com.onpositive.semantic.model.api.changes.ISetDelta<IPropertyGroup> dlt) {
			if (ignore){
				return;
			}
			onPropertyGroupsChanged();
			super.fireDelta(dlt);
		};
	};

	
	public OrderedRealm<IPropertyGroup> getPropertyGroups() {
		return pgroups;
	}
	public void updatePropertyGroups(IPropertyGroup[] newGroups){
		ignore=true;
		try{
		pgroups.remove(new ArrayList<IPropertyGroup>(pgroups.getContents()));
		pgroups.add(Arrays.asList(newGroups));
		}finally{
		ignore=false;
		}
		onPropertyGroupsChanged();
	}

	protected void onPropertyGroupsChanged() {
		
		((StoredMetadata)uid_prop).setVlaue((String) PROPERTY_GROUPS, pgroups.getContents().toArray(new IPropertyGroup[pgroups.size()]));
				
	}

	public IPropertyGroup getGroup(String group) {
		for (IPropertyGroup p : pgroups) {
			if (p.getId().equals(group)) {
				return p;
			}
		}
		return null;
	}

	protected void setActual(int num,boolean value) {
		collection.setValue(num,actuality, value);
	}

}